package com.yunhan.redis;

import org.redisson.api.RLock;
import org.springframework.data.redis.core.ZSetOperations;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;


/**
 * redis工具接口
 */
public interface RedisCache {

    /**
     * 需要序列化
     *
     * @param <T>
     * @param key1 缓存key
     * @param key2 缓存key
     * @return
     */
    <T> T getDoubleCache(String key1, String key2, Class<T> clazz);

    /**
     * 双缓存需要序列化，击穿1缓存异步构建2缓存
     * 缓存设置执行数据代理接口
     * key1（默认缓存2分钟）
     * key2（默认缓存1小时）
     *
     * @param key1          缓存key
     * @param cacheConsumer 业务执行
     * @param <T>
     * @return
     */
    <T> T getIfPresentDouble(String key1, String key2, CacheConsumer cacheConsumer, Class<T> clazz);


    /**
     * 双缓存需要序列化，击穿1缓存异步构建
     * 缓存设置执行数据代理接口
     *
     * @param key1          缓存Key1
     * @param expireTime1   key1过期时间
     * @param key2          缓存Key2
     * @param expireTime2   key2过期时间
     * @param cacheConsumer 执行逻辑
     * @param unit          时间单位
     * @param clazz         返回结果类型
     * @return
     */
    <T> T getIfPresentDouble(String key1, Long expireTime1, String key2, Long expireTime2, CacheConsumer cacheConsumer, TimeUnit unit, Class<T> clazz);

    /**
     * 双缓存需要序列化，击穿1缓存异步构建
     * 缓存设置执行数据代理接口
     * key1（默认缓存2分钟）
     * key2（默认缓存1小时）
     *
     * @param key1          缓存key
     * @param cacheConsumer 业务执行
     * @param <T>
     * @return
     */
    <T> T getIfPresentDoubleArray(String key1, String key2, CacheConsumer cacheConsumer, Class<T> clazz);


    /**
     * 双缓存需要序列化，击穿1缓存异步构建
     * 缓存设置执行数据代理接口
     *
     * @param key1          缓存Key1
     * @param expireTime1   key1过期时间
     * @param key2          缓存Key2
     * @param expireTime2   key2过期时间
     * @param cacheConsumer 执行逻辑
     * @param unit          时间单位
     * @param clazz         返回结果类型
     * @return
     */
    <T> T getIfPresentDoubleArray(String key1, Long expireTime1, String key2, Long expireTime2, CacheConsumer cacheConsumer, TimeUnit unit, Class<T> clazz);


    /**
     * 需要序列化
     * 缓存设置执行数据代理接口（默认缓存5分钟）
     *
     * @param <T>
     * @param key           缓存key
     * @param cacheConsumer 业务执行
     * @return
     */
    <T> T getIfPresent(String key, CacheConsumer cacheConsumer, Class<T> clazz);

    /**
     * 缓存设置执行数据代理接口
     *
     * @param key           缓存key
     * @param cacheConsumer 业务执行
     * @param expireTime    过期时间
     * @param unit          时间单位
     * @param <T>
     * @return
     */
    <T> T getIfPresent(String key, CacheConsumer cacheConsumer, Long expireTime, TimeUnit unit, Class<T> clazz);


    /**
     * 需要序列化
     * 缓存设置执行数据代理接口（默认缓存5分钟）
     *
     * @param <T>
     * @param key           缓存key
     * @param cacheConsumer 业务执行
     * @return
     */
    <T> T getIfPresentArray(String key, CacheConsumer cacheConsumer, Class<T> clazz);





    /**
     * 缓存设置执行数据代理接口(阻塞锁)
     *
     * @param key            缓存key
     * @param lockKey        击穿锁
     * @param lockExpireTime 锁过期时间
     * @param lockUnit       时间单位
     * @param cacheConsumer  业务执行
     * @param expireTime
     * @param unit           时间单位
     * @param clazz
     * @param <T>
     * @return
     */
    <T> T getIfPresentForLock(String key, String lockKey, Long lockExpireTime, TimeUnit lockUnit, CacheConsumer cacheConsumer, Long expireTime, TimeUnit unit, Class<T> clazz);


    /**
     * 缓存设置执行数据代理接口(幂等锁)
     *
     * @param key            缓存key
     * @param lockKey        击穿锁
     * @param lockExpireTime 锁过期时间
     * @param lockUnit       时间单位
     * @param cacheConsumer  业务执行
     * @param expireTime
     * @param unit           时间单位
     * @param clazz
     * @param <T>
     * @return
     */
    <T> T getIfPresentForTryLock(String key, String lockKey, Long lockExpireTime, TimeUnit lockUnit, CacheConsumer cacheConsumer, Long expireTime, TimeUnit unit, Class<T> clazz);


    /**
     * 双缓存需要序列化，击穿1缓存异步构建
     * 缓存设置执行数据代理接口（锁）
     *
     * @param key1           缓存Key1
     * @param expireTime1    key1过期时间
     * @param key2           缓存Key2
     * @param expireTime2    key2过期时间
     * @param lockKey        锁
     * @param lockExpireTime 锁过期时间
     * @param cacheConsumer  执行逻辑
     * @param unit           时间单位
     * @param clazz          返回结果类型
     * @return
     */
    <T> T getIfPresentDoubleForLock(String key1, Long expireTime1, String key2, Long expireTime2, String lockKey, Long lockExpireTime, CacheConsumer cacheConsumer, TimeUnit unit, Class<T> clazz);


//--------------------------------------------
//        缓存设置模块
//--------------------------------------------

    /**
     * 设置对象缓存（默认5分钟）
     *
     * @param key key
     * @param obj 对象
     */
    void set(String key, Object obj);

    /**
     * 设置对象缓存（自定义过期时间）
     *
     * @param key    key
     * @param obj    对象
     * @param expiry 过期时间
     * @param unit   时间单位
     */
    void set(String key, Object obj, long expiry, TimeUnit unit);


    /**
     * 设置字符串缓存
     * @param key key
     * @param str 字符串
     */
    void set(String key, String str);

    /**
     * 设置int值
     * @param key
     * @param val
     */
    void setInt(String key, Integer val);

    /**
     * 设置字符串缓存（指定日期到期）
     * @param key key
     * @param str 字符串
     * @param date 到期日期
     */
    void set(String key, String str, Date date);


    /**
     * 设置字符串缓存（自定义过期时间）
     *
     * @param key     key
     * @param str 值
     * @param expiry  过期时间
     * @param unit    时间单位
     */
    void set(String key, String str, long expiry, TimeUnit unit);


    /**
     * 把数据放入到hash中（自定义过期时间）
     * @param key  key
     * @param hKey hash的key
     * @param val  值
     * @param time 过期时间
     */
    void hSet(String key, String hKey, String val, long time, TimeUnit unit);

    /**
     * 把数据放入到hash中（指定日期过期）
     * @param key  key
     * @param hKey hash的key
     * @param val  值
     * @param date 过期时间
     */
    void hSet(String key, String hKey, String val, Date date);


    /**
     * set集合添加多个字符串元素
     * @param key
     * @param vals
     */
    Long sAdd(String key, String[] vals);

    /**
     * set集合添加元素
     * @param key
     * @param vals
     */
    Long sAdd(String key, String vals);

    /**
     * 有序集合
     * @param key
     * @param vals
     * @return
     */
    Long szAdd(String key, Set<ZSetOperations.TypedTuple<String>> vals);

    /**
     * 有序集合
     * @param key
     * @param val
     * @return
     */
    boolean szAdd(String key, String val, Double score);

    /**
     * TODO 移除有序集合元素
     * @param key
     * @return
     */
    String szPop(String key);

    /**
     * 设置过期时间
     * @param key
     * @param unit
     * @param timeout
     */
    long setTtl(String key, TimeUnit unit, long timeout);

//--------------------------------------------
//        缓存获取模块
//--------------------------------------------

    /**
     * 获取缓存
     * @param key key
     * @return 返回值字符串
     */
    String get(String key);

    /**
     * 获取缓存
     * @param key    key
     * @param clazz 类
     * @return 返回值类型自定义
     */
    <T> T get(String key, Class<T> clazz);


    /**
     * 获取缓存
     * @param key    key
     * @param clazz 类
     * @return 返回值类型为List集合
     */
    <T> List<T> gets(String key, Class<T> clazz);


    /**
     * 获取hash的key
     * @param key  key
     * @param hKey hash的key
     * @return 返回值
     */
    Object hGet(String key, String hKey);


    /**
     * 获取整个hash
     * @param key key
     * @return 返回值
     */
    Map<Object, Object> hGet(String key);


    /**
     * 获取set集合
     * @param key set集合的Key
     * @return set集合
     */
    Set<String> members(String key);


    /**
     * 从redis中获取key对应的过期时间;
     * 如果该值有过期时间，就返回相应的过期时间;
     * 如果该值没有设置过期时间，就返回-1;
     * 如果没有该值，就返回-2;
     * @param key key
     */
    Long getExpire(String key);
    /**
     * 从有序集合中获取指定分数范围内的元素
     *
     * @param key       缓存key
     * @param minScore  最小分数
     * @param maxScore  最大分数
     * @param offset    偏移量
     * @param count     返回数量
     * @return 返回的元素集合
     */
    Set<ZSetOperations.TypedTuple<String>> rangeByScoreWithScores(String key, double minScore, double maxScore, long offset, long count);



//--------------------------------------------
//        删除缓存模块
//--------------------------------------------

    /**
     * 删除key
     * @param key key
     */
    void remove(String key);

    /**
     * 批量删除key
     * @param keys key
     */
    void remove(List<String> keys);

    /**
     * 删除hash中的key
     * @param key  key redis的key
     * @param hKey hkey hash中的key
     */
    void hRomve(String key, String hKey);

    /**
     * 批量删除hash中的key
     * @param key   key
     * @param hKeys hkey
     */
    void hRomves(String key, String... hKeys);

    /**
     * 删除set集合中指定元素
     * @param key set集合的key
     * @param obj 要删除的集合元素
     */
    long sRemove(String key, Object obj);


    /**
     * 删除zset中的数据
     * @param key
     * @param value
     * @return
     */
    long zRemove(String key, String value);
    /**
     * set集合随机移除一个元素，并返回
     * @param key
     * @param clazz
     * @return
     */
    <T> T sPop(String key, Class<T> clazz);

    /**
     * set集合随机移除一个字符串，并返回
     * @param key
     * @return
     */
    String sPopStr(String key);

    /**
     * 获取set集合元素
     * @param key
     * @return
     */
    List<String> sMembers(String key);

    /**
     * set集合随机获取多个元素并去重（该api有bug）
     * @param key
     * @return
     */
    <T>List<T> sGetAndDistinct(String key, Integer num, Class<T> _class);

    /**
     * 获取set集合长度
     * @return
     */
    Integer size(String key);

    /**
     * set集合随机获取一个元素
     * @param key
     * @return
     */
    String sGet(String key);

    /**
     * set集合随机移除多个元素，并返回
     * @param key
     * @param count
     * @return
     */
    <T> List<T> sPopList(String key, long count, Class<T> _class);

//--------------------------------------------
//        其他缓存操作
//--------------------------------------------

    /**
     * 判断key是否存在
     * @param key 参数
     * @return 返回值
     */
    Boolean exists(String key);

    /**
     * 发布消息
     * @param channel
     * @param message
     */
    void publish(String channel, String message);

    /**
     * set集合判断元素是否存在
     * @param key
     * @param value
     * @return
     */
    Boolean isMember(String key, Object value);


    /**
     * setIfAbsent：尝试向redis中设置key，key不存在则设置，存在则设置不设置
     * @param key  key
     * @param val  value
     * @param time 过期时间
     * @return 返回值
     */
    boolean setIfAbsent(String key, String val, long time, TimeUnit unit);

    /**
     * setIfAbsent：尝试向hash中设置hkey，hkey不存在则设置，存在则设置不设置
     * @param key  key
     * @param hkey  hkey
     * @param val  hvalue
     * @param time key的过期时间
     * @return 返回值
     */
    boolean hSetIfAbsent(String key, String hkey, String val, long time, TimeUnit unit);


    /**
     * lock-锁hkey
     * @param key  key
     * @param hkey  hkey
     * @param val  值
     * @param time key的过期时间
     * @return 返回值
     */
    boolean hLock(String key, String hkey, String val, long time, TimeUnit unit);


    /**
     * 原子增长
     * @param key
     * @param value
     * @param time
     * @return
     */
    long incrBy(String key, long value, long time, TimeUnit unit);

    /**
     * 原子增长
     * @param key
     * @param value
     * @return
     */
    Integer incrBy(String key, Integer value);

    /**
     * 原子扣减
     * @param key
     * @param value
     * @return 扣减后的值
     */
    Integer decrBy(String key, Integer value);

    /**
     * hash的原子增长
     * @param key  hash的key
     * @param hKey key
     * @param val  值
     * @return 返回值
     */
    long hIncrBy(String key, String hKey, long val, long time, TimeUnit unit);



    /**
     * 获取当前原子变量值（不存在会默认设置0）
     * @param key
     * @param time
     * @return
     */
    long currentIncrby(String key, long time, TimeUnit unit);

    /**
     * 获取当前Hash原子变量值（不存在会默认设置0）
     * @param key
     * @param time
     * @return
     */
    long currentHashIncrby(String key, String filed, long time, TimeUnit unit);



//--------------------------------------------
//        批量获取缓存操作
//--------------------------------------------

    /**
     * 管道批量执行(仅支持同一类型)
     */
    <T> List<T> batchGets(List<String> keys, Class<T> _class);

    /**
     * 管道批量执行
     */
    List<Object> pipelinedSetMembers(String key, List<String[]> values, long timeOut, TimeUnit unit);

    /**
     * 管道批量执行(仅支持同一类型)
     */
    List<String> batchGets(List<String> keys);

    /**
     * 批量执行(仅支持同一类型)
     */
    <T> List<T> mGets(List<String> keys, Class<T> _class);

    /**
     * 批量执行(仅支持同一类型)
     */
    List<String> mGets(List<String> keys);

    /**
     * 获取列表指定范围内的元素
     *
     * @param key
     * @param start 开始位置, 0是开始位置
     * @param end   结束位置, -1返回所有
     * @return
     */
    List<String> lRange(String key, long start, long end);


    /**
     * 管道批量执行删除
     */
    void batchDel(List<String> keys);




//--------------------------------------------
//        锁操作
//--------------------------------------------

    /**
     * 阻塞直到加锁成功，且加锁成功后不会自动释放
     * @param lockKey
     * @param unit
     * @param timeout
     */
    RLock lock(String lockKey, TimeUnit unit, long timeout);

    /**
     * setNX操作
     * @param key  参数
     * @param time 参数
     * @return 返回值
     */
    boolean lockNX(String key, String val, long time, TimeUnit unit);

    /**
     * 尝试加锁-超过最大等待时间则返回
     * @param lockKey
     * @param unit
     * @param waitTime  获取锁等待时间
     * @param leaseTime 释放锁时间，最大持有锁的时间
     * @return
     */
    boolean tryLock(String lockKey, TimeUnit unit, long waitTime, long leaseTime);

    /**
     * 尝试加锁-连锁机制，所有key成功加锁才算成功（redis集群部署时需要使用该机制）
     * @param lockKeys
     * @param unit
     * @param waitTime  获取锁等待时间
     * @param leaseTime 释放锁时间
     * @return
     */
    boolean multiLockTryLock(List<String> lockKeys, TimeUnit unit, long waitTime, long leaseTime);

    /**
     * 连锁-无等待时间
     * @param lockKeys
     * @param unit
     * @param leaseTime 释放锁时间
     * @return
     */
    default boolean multiLockTryLock(List<String> lockKeys, TimeUnit unit, long leaseTime) {
        return multiLockTryLock(lockKeys, unit, 0, leaseTime);
    }
    /**
     * 尝试加锁-无等待时间
     * @param lockKey
     * @param unit
     * @param leaseTime 释放锁时间
     * @return
     */
    default boolean tryLock(String lockKey, TimeUnit unit, long leaseTime) {
        return tryLock(lockKey, unit, 0, leaseTime);
    }



    /**
     * multiLock 解锁
     * @param lockKeys
     */
    void multiLockUnlock(List<String> lockKeys);

    /**
     * 释放锁
     * @param lockKey
     */
    void unlock(String lockKey);

    /**
     * 获取有序集合的数量
     *
     * @param key 缓存key
     * @return 返回有序集合的数量
     */
    Long zSize(String key);
}
